#ifndef DESCENT_DETAIL_ATTRIBUTE_HPP_INCLUDED
#define DESCENT_DETAIL_ATTRIBUTE_HPP_INCLUDED

/**
 * @file 
 */

#include <cstdint>
#include <string>
#include <boost/any.hpp>
#include <boost/filesystem.hpp>
#include <boost/static_assert.hpp>
#include <mocha/color.hpp>
#include <mocha/enum_string.hpp>
#include <mocha/math.hpp>
#include "descent/log.hpp"

namespace descent {
/**
 * Attribute data types.
 */
enum attrib_data_type {
	/// 32-bit signed integer attribute.
	attrib_type_integer,
	/// Arbitrary floating point attribute. (Precision is not specified.)
	attrib_type_real,
	/// Two-dimensional vector (of real values) attribute.
	attrib_type_vector,
	/// 4-by-4 matrix (of real values) attribute.
	attrib_type_matrix,
	/// 4-channel 32-bit color attribute.
	attrib_type_color,
	/// Text/string attribute.
	attrib_type_text,
	/// File system path attribute.
	attrib_type_path
};
/**
 * Attribute traits.
 * Specializations contain information about attributes based on a type enumerator.
 * @param T Attribute type enumerator.
 */
template<attrib_data_type T>
struct attrib_traits { BOOST_STATIC_ASSERT((sizeof(T) == 0)); };
template<>
struct attrib_traits<attrib_type_integer> {
	typedef std::int32_t native_type;
};
template<>
struct attrib_traits<attrib_type_real> {
	typedef float native_type;
};
template<>
struct attrib_traits<attrib_type_vector> {
	typedef mocha::fvec2 native_type;
};
template<>
struct attrib_traits<attrib_type_matrix> {
	typedef mocha::fmat44 native_type;
};
template<>
struct attrib_traits<attrib_type_color> {
	typedef mocha::graphics::color native_type;
};
template<>
struct attrib_traits<attrib_type_text> {
	typedef std::string native_type;
};
template<>
struct attrib_traits<attrib_type_path> {
	typedef fs::path native_type; // TODO: We could go further and use a more abstracted type that transparently supports archives. Also, paths should support multiple roots, for example /usr/local/games/descent/data as well as ~/.descent/data.
};
/**
 * Parses an attribute from text.
 * @param T Attribute type enumerator.
 * @param text Text to parse.
 * @return Attribute encoded in the text. A default-constructed attribute is returned if there is an error.
 */
template<attrib_data_type T>
typename attrib_traits<T>::native_type parse_attribute(const std::string& text) {
	try {
		return boost::lexical_cast<typename attrib_traits<T>::native_type>(text);
	}
	catch (const boost::bad_lexical_cast&) {
		log().warning() << "failed to parse " << mocha_enum_string<attrib_data_type>::to_string(T()) << " attribute \"" << text << "\"\n";
		return typename attrib_traits<T>::native_type(); // Default-construct the attribute.
	}
}
template<>
typename attrib_traits<attrib_type_vector>::native_type parse_attribute<attrib_type_vector>(const std::string& text) {
	// TODO: Extract the components and parse them as reals. Format is "{ x, y }".
}
template<>
typename attrib_traits<attrib_type_matrix>::native_type parse_attribute<attrib_type_matrix>(const std::string& text) {
	// TODO: Extract the elements and parse them as reals. Format is "{ { a11, a12, a13, a14 }, ..., { a41, a42, a43, a44 } }".
}
template<>
typename attrib_traits<attrib_type_color>::native_type parse_attribute<attrib_type_color>(const std::string& text) {
	// TODO: Strip the "#" prefix and parse the hexidecimal value.
}
template<>
typename attrib_traits<attrib_type_text>::native_type parse_attribute<attrib_type_text>(const std::string& text) {
	return text; // Just pass the text through. Hopefully, compilers can inline this.
}
template<>
typename attrib_traits<attrib_type_path>::native_type parse_attribute<attrib_type_path>(const std::string& text) {
	return text; // Just pass the text through; it should automatically construct a path. // TODO: Maybe the path should be tested for validity here.
}
}
MOCHA_ENUM_STRING_BEGIN(descent::attrib_data_type) {
	MOCHA_ENUM_STRING(descent::attrib_type_integer);
	MOCHA_ENUM_STRING(descent::attrib_type_real);
	MOCHA_ENUM_STRING(descent::attrib_type_vector);
	MOCHA_ENUM_STRING(descent::attrib_type_matrix);
	MOCHA_ENUM_STRING(descent::attrib_type_color);
	MOCHA_ENUM_STRING(descent::attrib_type_text);
	MOCHA_ENUM_STRING(descent::attrib_type_path);
	MOCHA_ENUM_ALIAS(descent::attrib_type_integer, "integer");
	MOCHA_ENUM_ALIAS(descent::attrib_type_real, "real");
	MOCHA_ENUM_ALIAS(descent::attrib_type_vector, "vector");
	MOCHA_ENUM_ALIAS(descent::attrib_type_matrix, "matrix");
	MOCHA_ENUM_ALIAS(descent::attrib_type_color, "color");
	MOCHA_ENUM_ALIAS(descent::attrib_type_text, "text");
	MOCHA_ENUM_ALIAS(descent::attrib_type_path, "path");
} MOCHA_ENUM_STRING_END;

#endif

